<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      html,
      body {
        height: 100vh;
        overflow: hidden;
        padding: 0px;
        margin: 0px;
      }
      #container {
        width: 100%;
        height: 100%;
      }
      .btns {
        position: absolute;
        top: 10px;
        left: 60px;
      }
      table {
        width: 800px;
        position: absolute;
        bottom: 20px;
        left: calc(50% - 400px);
        border-collapse: collapse;
      }
      table th {
        text-align: left;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>

    <div class="btns">
      <input type="file" id="file-input" />
      <!-- <button id="startWork">天车运行</button> -->
    </div>

    <table id="table" border="1"></table>

    <!-- <script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script> -->
    <script src="node_modules/xlsx/dist/xlsx.full.min.js"></script>
    <script src="node_modules/mathjs/lib/browser/math.js"></script>
    <!-- <script src="https://unpkg.com/mathjs"></script> -->
    <script type="module">
      import * as THREE from "./js/three.module.js";
      import { OrbitControls } from "./js/OrbitControls.js";
      import { TransformControls } from "./js/TransformControls.js";
      import { GLTFLoader } from "./js/GLTFLoader.js";
      import { DDSLoader } from "./js/DDSLoader.js";
      import { TWEEN } from "./js/tween.module.min.js";
      import { FBXLoader } from "./js/FBXLoader.js";
      import { getData, productList, structuralData } from "./index.js";

      let container, camera, scene, renderer, orbit, raycaster, control;
      let manager;
      const objArr = [
        "chuan",
        "chuanwu",
        "longmendiao",
        "diaogou01",
        "diaogou02",
      ];
      // const objArr = ['chuan','chuanwu','longmendiao','qizhongji01','qizhongji02'];
      // 'diaogou01','diaogou02'
      const modelMap = {};
      let geometryLine;
      let moveState = false;
      let intersectsObjs = [];
      let mixers = [];
      let clock = new THREE.Clock();
      let partData = [];
      let nextData = {};

      let lmd;
      let diaogou;
      let downMixer, downAction;
      let upMixer, upAction;

      // const lmdSpeed = 0.14;
      // const dgSpeed = 0.0693;
      const lmdSpeed = 0.7;
      const dgSpeed = 0.3465;
      init();
      animate();

      let diaogouBone;
      let targetMaterial;
      let targetPosition;
      let targetAssemble;

      let arr = [];
      let progress;

      let tableData = [];

      document
        .getElementById("file-input")
        .addEventListener("change", function (e) {
          getData(e).then((data) => {
            // console.log("-----处理表格-----", data);
            partData = data;
            startWork();
            // product()
          });
        });

      function init() {
        container = document.getElementById("container");
        camera = new THREE.PerspectiveCamera(
          45,
          window.innerWidth / window.innerHeight,
          1,
          100000
        );
        camera.position.set(0, 1000, 800);
        camera.lookAt(0, 0, 0);

        scene = new THREE.Scene();
        scene.add(new THREE.AmbientLight(0xffffff));

        const ambientLight = new THREE.AmbientLight(0xffffff); // 颜色为浅灰色
        scene.add(ambientLight);

        const light = new THREE.DirectionalLight(0xffffff, 1.5);
        light.position.set(0, 3000, 0);
        scene.add(light);

        // const axes = new THREE.AxesHelper(500);
        // scene.add(axes);

        raycaster = new THREE.Raycaster();

        renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);
        container.appendChild(renderer.domElement);

        orbit = new OrbitControls(camera, renderer.domElement);
        orbit.addEventListener("change", render);
        orbit.object.position.set(
          -827.3215836298397,
          384.3064172641637,
          387.0092444125056
        );
        orbit.update();

        control = new TransformControls(camera, renderer.domElement);
        control.addEventListener("change", render);
        control.addEventListener("mouseDown", (ele, event) => {
          console.log(ele.target.object.position);
        });
        control.addEventListener("mouseUp", (ele) => {
          var mesh = ele.target.object;
          if (mesh.target) {
            console.log(ele.target.object.position);
            mesh.target.position.x = ele.target.object.position.x;
            mesh.target.position.y = ele.target.object.position.y;
            mesh.target.position.z = ele.target.object.position.z;
          }
        });
        control.addEventListener("dragging-changed", function (event) {
          orbit.enabled = !event.value;
        });
        scene.add(control);

        manager = new THREE.LoadingManager(modelLoaded);
        manager.addHandler(/\.dds$/i, new DDSLoader());
        loadModels();

        container.addEventListener("mousedown", onDocumentMouseDown, false);

        window.addEventListener("resize", onWindowResize, false);
      }

      let num = -22;
      let state = true;

      function animate() {
        requestAnimationFrame(animate);
        TWEEN.update();
        // 钢索伸缩
        // if(geometryLine && moveState){
        //   if(state){
        //     num -= 0.01;
        //     modelMap['Hooklock'].position.y-=0.01
        //   }else{
        //     num+= 0.01
        //     modelMap['Hooklock'].position.y+=0.01
        //   }

        //   if(num < - 60){
        //     state = false
        //     moveState = false
        //   }

        //   if(num > - 20){
        //     state = true
        //     moveState = false
        //     // assemble()
        //     // 添加
        //     // addParts()
        //   }
        //   const endPoint = new THREE.Vector3(0, num, 0);
        //    // 更新钢索长度以实现伸缩效果
        //   // const length = Math.sin(Date.now() / 1000) * 5 + 5; // 使用正弦波来模拟伸缩效果
        //   // const endPoint = new THREE.Vector3(0, -length, 0); // 根据当前时间更新末端点位置
        //   geometryLine.setFromPoints([new THREE.Vector3(0, 0, 0), endPoint]); // 更新线段几何体
        //   geometryLine.verticesNeedUpdate = true; // 通知Three.js需要更新顶点数据
        //   geometryLine.computeBoundingSphere(); // 重置边界球体，确保正确渲染
        // }

        // if (mixers.length) {
        //   const delta = clock.getDelta();
        //   for (const mixer of mixers) {
        //     mixer.update(delta);
        //   }
        // }

        // renderer.render(scene, camera);
        render();
      }

      function render() {
        renderer.render(scene, camera);

        const delta = clock.getDelta();
        if (mixers.length) {
          mixers.forEach((mixer) => {
            mixer.update(delta);
          });
          if (targetMaterial) {
            // 物料复制吊钩的坐标，跟随移动
            const boneMatrixWorld = diaogouBone.matrixWorld;
            const position = new THREE.Vector3();
            boneMatrixWorld.decompose(
              position,
              new THREE.Quaternion(),
              new THREE.Vector3()
            );
            targetMaterial.position.set(
              position.x,
              position.y - 15.6955,
              position.z
            );

            // 吊钩下降过程中，物料到达指定位置，则停止下降
            if (downAction._state === "put") {
              if (targetMaterial.position.y <= targetPosition.y) {
                downAction.halt();
                let timer = setTimeout(() => {
                  clearTimeout(timer);
                  timer = null;

                  downAction._state = "";
                  upAction.reset();
                  upAction.time = 2 - downAction.time;
                  upAction.play();
                  if (progress === "assembly") {
                    // 隐藏
                    targetAssemble.visible = false;
                  }
                }, 500);
                targetMaterial = null;
              }
            }
          }
        }
      }

      function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      }

      function loadModels() {
        for (let j = 0; j < objArr.length; j++) {
          const str = objArr[j];
          const gltfPath = `./models/${str}/${str}.gltf`;
          new GLTFLoader(manager).load(gltfPath, function (gltf) {
            const obj = gltf.scene;
            obj.animations = gltf.animations;
            modelMap[str] = obj;
            obj.name = str;
            scene.add(obj);
          });
        }
      }

      // 加载完成
      function modelLoaded() {
        console.log(modelMap);
        const chuan = modelMap["chuan"];
        chuan.position.set(-120.90269128818719, 0, 204.01663856841677);
        chuan.scale.setScalar(0.8);

        const chuanPos = [
          [-120.90269128818719, 0, -47.192358242533246],
          [-91.82663052587066, 0, 476.82583266316504],
          [-91.82663052587066, 0, 224.24587030635018],
        ];

        for (let i = 0; i < 3; i++) {
          const clone = chuan.clone(true);
          clone.position.set(...chuanPos[i]);
          scene.add(clone);
          if (i === 1 || i === 2) {
            clone.rotation.y = Math.PI;
          }
        }

        diaogou = modelMap["diaogou02"];
        diaogou.name = "diaogou02";
        const sz = diaogou.getObjectByName("02_1");
        diaogouBone = sz.skeleton.bones[1];

        lmd = new THREE.Group();
        lmd.name = "lmd";
        modelMap["longmendiao"].scale.set(1.0655, 1, 1);
        lmd.add(modelMap["longmendiao"]);
        lmd.add(modelMap["diaogou01"]);
        lmd.add(modelMap["diaogou02"]);
        diaogou.position.set(0, 0, 0);
        scene.add(lmd);
        const box = new THREE.Box3().setFromObject(modelMap["longmendiao"]);
        const w = box.max.z - box.min.z;
        const offset = box.min.z + w / 2;
        lmd.offset = offset;
        lmd.position.set(-52.69281411763272, 0, 0);

        const _box = new THREE.Box3().setFromObject(diaogou);
        const l = _box.max.x - _box.min.x;
        const _offset = _box.min.x + l / 2;
        diaogou.offset = _offset;

        // 下降动画
        downMixer = new THREE.AnimationMixer(diaogou);
        downAction = downMixer.clipAction(diaogou.animations[0]);
        downAction.setLoop(THREE.LoopOnce);
        downAction.clampWhenFinished = true;
        downAction.timeScale = 3;
        mixers.push(downMixer);

        // 上升动画
        upMixer = new THREE.AnimationMixer(diaogou);
        upAction = upMixer.clipAction(diaogou.animations[1]);
        upAction.setLoop(THREE.LoopOnce);
        upAction.clampWhenFinished = true;
        upAction.timeScale = 3;
        mixers.push(upMixer);
      }

      //  计算方法
      function fitRectangles(containerWidth, containerHeight, rectangles) {
        rectangles.sort((a, b) => a.width - b.width); // 按宽度排序
        let row = []; // 当前行的矩形列表和它们的结束位置
        let totalCount = 0; // 放置的矩形总数
        let currentRowHeight = 0; // 当前行的最大高度

        for (let rect of rectangles) {
          if (row.length === 0 || rect.width <= row[row.length - 1].end) {
            // 可以放入当前行
            // 更新当前行的信息，包括新加入的矩形及其结束位置（当前位置加上矩形的宽度）
            row.push({ rect, end: currentRowHeight + rect.height });
            currentRowHeight = Math.max(currentRowHeight, rect.height); // 更新行高
          } else {
            // 换到下一行，使用二分查找确定最佳位置插入当前矩形
            let left = 0,
              right = row.length;
            while (left < right) {
              let mid = Math.floor((left + right) / 2);
              if (row[mid].end <= currentRowHeight) {
                // 可以放入当前行中间的位置
                left = mid + 1;
              } else {
                right = mid;
              }
            }
            // 在找到的位置插入当前矩形，并更新当前行的信息
            row.splice(left, 0, { rect, end: currentRowHeight + rect.height });
            currentRowHeight = Math.max(currentRowHeight, rect.height); // 更新行高
          }
          totalCount++; // 增加计数器
        }
        return totalCount; // 返回可以放置的最大矩形数
      }

      function maximizeArea(regions, objects) {
        // 排序区域和物体
        regions.sort((a, b) => a - b);
        objects.sort((a, b) => b - a);

        // 初始化区域面积数组
        let regionAreas = regions.map((r) => 0);

        // 分配物体到区域
        for (let obj of objects) {
          // 找到当前未满区域中面积最小的区域
          let minIndex = regionAreas.indexOf(Math.min(...regionAreas));
          regionAreas[minIndex] += obj;
        }

        // 返回最终的区域面积数组
        return regionAreas;
      }

      // 示例数据
      let regions = [50, 30, 20]; // 三个区域的面积
      let objects = [15, 25, 10, 35]; // 物体的面积列表

      // 调用函数并打印结果
      let result = maximizeArea(regions, objects);
      // console.log("分配后的区域面积：", result);

      // const math = require('mathjs');
      // 定义决策变量（这里是三个区域的面积）和目标函数（这里是最大化总面积）
      // let variables = ['x1', 'x2', 'x3']; // x1, x2, x3 分别代表三个区域的面积
      // let objective = ['max', [50, 'x1'], [30, 'x2'], [20, 'x3']]; // 目标函数：max(50*x1 + 30*x2 + 20*x3)
      // let constraints = [ // 约束条件：每个物体的面积不超过对应区域的容量（此处简化，实际应用中可能需要更复杂的约束）
      //     ['<=', [15, 'x1']], ['<=', [25, 'x2']], ['<=', [10, 'x3']], ['<=', [35, 'x4']] // x4 为额外空间示意，实际应用中应有对应约束条件
      // ];

      // // 使用数学库求解线性规划问题
      // let solution = math.optimize.lp(objective, constraints, variables);
      // console.log("最优解：", solution); // 输出最优解及其值

      /**
       * 计算在三个面积不同的区域堆放物体的最优解（最大化物体总数）
       * @param {number[]} areas - 三个区域的面积数组 [area1, area2, area3]
       * @param {number[]} densities - 三个区域的堆放密度数组 [density1, density2, density3]（单位面积可堆放物体数）
       * @returns {Object} 包含总堆放量和各区域堆放量的对象
       */
      function optimizeStorage(areas, densities) {
        // 验证输入
        if (areas.length !== 3 || densities.length !== 3) {
          throw new Error("必须提供三个区域的面积和密度数据");
        }
        if (
          areas.some((area) => area <= 0) ||
          densities.some((density) => density <= 0)
        ) {
          throw new Error("面积和密度必须是正数");
        }

        // 创建区域索引数组 [0, 1, 2]
        const indices = [0, 1, 2];

        // 按堆放密度降序排序（优先使用高密度区域）
        indices.sort((a, b) => densities[b] - densities[a]);

        // 初始化结果
        const result = {
          totalItems: 0,
          allocations: [0, 0, 0],
        };

        // 贪心算法：按密度从高到低分配
        for (const index of indices) {
          const capacity = areas[index] * densities[index];
          result.allocations[index] = capacity;
          result.totalItems += capacity;
        }

        return result;
      }

      // 示例用法
      const areas = [100, 150, 200]; // 三个区域的面积
      const densities = [0.8, 1.2, 0.5]; // 单位面积堆放数量（密度）

      try {
        const solution = optimizeStorage(areas, densities);
        console.log("最优解详情:");
        console.log(`区域1堆放: ${solution.allocations[0].toFixed(1)} 个物体`);
        console.log(`区域2堆放: ${solution.allocations[1].toFixed(1)} 个物体`);
        console.log(`区域3堆放: ${solution.allocations[2].toFixed(1)} 个物体`);
        console.log(`总堆放量: ${solution.totalItems.toFixed(1)} 个物体`);
      } catch (error) {
        console.error("计算错误:", error.message);
      }

      function createTextCanvas(
        shipName,
        text,
        color = "black",
        fontSize = 30
      ) {
        const width = 300,
          height = 300;
        const canvas = document.createElement("canvas");
        canvas.width = width;
        canvas.height = height;
        const context = canvas.getContext("2d");
        // context.fillStyle = 'transparent';
        context.fillStyle = color;
        context.fillRect(0, 0, width, height);

        context.font = `${fontSize}px Arial`;
        // context.fillStyle = 'transparent';
        // context.fillStyle = '#2891FF'
        context.fillStyle = "blue";
        context.fillText(shipName, 20, 30); // 调整位置和大小以适应你的需求

        context.fillStyle = "blue";
        context.fillText(text, 20, 60); // 调整位置和大小以适应你的需求

        let url = canvas.toDataURL("image/png");
        return url;
      }

      function getRandomColor() {
        var letters = "0123456789ABCDEF";
        var color = "#";

        for (var i = 0; i < 6; i++) {
          color += letters[Math.floor(Math.random() * 16)];
        }

        return color;
      }

      function product() {
        productList[4].shipList.forEach((shipItem) => {
          shipItem.partlist.forEach((partItem) => {
            // 判断是不是总段
            let curList = [];
            let type = "";
            const parentData = structuralData.find(
              (itemS) => itemS.parentNumber === partItem.partNumber
            );
            if (parentData?.childList?.length) {
              curList = parentData.childList;
            } else {
              curList.push(partItem.partNumber);
            }

            if (curList.length) {
              console.log("++++++AAAADDDD++++++++++", curList);
              let max = partData.find(
                (itemP) => curList[0] === itemP["分段号"]
              );
              curList.forEach((curId, j) => {
                const cur = partData.find((itemP) => curId === itemP["分段号"]);
                const maxL = max["分段长度_1"];
                const maxW = max["分段宽度_1"];

                const curL = cur["分段长度_1"];
                const curW = cur["分段宽度_1"];
                const curH = cur["分段高度_1"];
                if (curL * curW > maxL * maxW) {
                  max = cur;
                }

                if (curList.length - 1 === j) {
                  const shipName = shipItem.shipNumber;
                  const name = partItem.partNumber;
                  const L = max["分段长度_1"];
                  const W = max["分段宽度_1"];
                  const H = max["分段高度_1"];
                  console.log("+++++EEEEE面积@@@###++++++", L * W);
                  createParts(
                    shipName,
                    name,
                    L,
                    W,
                    H,
                    partItem.position,
                    partItem.rotation
                  );
                }
              });
            }
          });
        });
      }

      // 开始吊装物料安装
      function startAssembly() {}

      // 开始起吊摆放物料
      function startWork() {
        const shipItem = productList[4].shipList[0];
        const partItem = shipItem.partlist[0];
        // 判断是不是总段
        let curList = [];
        let type = "startWork";
        const parentData = structuralData.find(
          (itemS) => itemS.parentNumber === partItem.partNumber
        );
        if (parentData?.childList?.length) {
          curList = parentData.childList;
        } else {
          curList.push(partItem.partNumber);
        }

        nextData = {
          shipListIdx: 0,
          shipListlen: productList[4].shipList.length,
          partlistIdx: 0,
          partlistlen: shipItem.partlist.length,
        };

        if (curList.length) {
          let max = partData.find((itemP) => curList[0] === itemP["分段号"]);
          curList.forEach((curId, j) => {
            const cur = partData.find((itemP) => curId === itemP["分段号"]);
            const maxL = max["分段长度_1"];
            const maxW = max["分段宽度_1"];

            const curL = cur["分段长度_1"];
            const curW = cur["分段宽度_1"];
            const curH = cur["分段高度_1"];
            if (curL * curW > maxL * maxW) {
              max = cur;
            }

            if (curList.length - 1 === j) {
              const shipName = shipItem.shipNumber;
              const name = partItem.partNumber;
              const L = max["分段长度_1"];
              const W = max["分段宽度_1"];
              const H = max["分段高度_1"];
              updateTableData(shipItem, partItem);
              createParts(shipName, name, L, W, H, partItem);
            }
          });
        }
      }

      // 查询下一个起吊物料
      async function findNext() {
        let a = "";
        let b = "";
        const nextPartIdx = nextData.partlistIdx + 1;
        const nextShipIdx = nextData.shipListIdx + 1;
        if (
          nextData.shipListIdx === nextData.shipListlen - 1 &&
          nextPartIdx > nextData.partlistlen - 1
        ) {
          // 可触发装配
          progress = "assembly";
          for (const key of arr) {
            await callLMD(key.obj, key.pos);
          }
          return;
        }

        if (nextPartIdx <= nextData.partlistlen - 1) {
          a = nextPartIdx;
          b = nextData.shipListIdx;
        } else if (nextShipIdx <= nextData.shipListlen - 1) {
          a = 0;
          b = nextShipIdx;
        }

        const shipItem = productList[4].shipList[b];
        const partItem = shipItem.partlist[a];

        nextData = {
          shipListIdx: b,
          shipListlen: productList[4].shipList.length,
          partlistIdx: a,
          partlistlen: shipItem.partlist.length,
        };
        // 判断是不是总段
        let curList = [];
        let type = "startWork";
        const parentData = structuralData.find(
          (itemS) => itemS.parentNumber === partItem.partNumber
        );
        if (parentData?.childList?.length) {
          curList = parentData.childList;
        } else {
          curList.push(partItem.partNumber);
        }

        if (curList.length) {
          let max = partData.find((itemP) => curList[0] === itemP["分段号"]);
          curList.forEach((curId, j) => {
            const cur = partData.find((itemP) => curId === itemP["分段号"]);
            const maxL = max["分段长度_1"];
            const maxW = max["分段宽度_1"];

            const curL = cur["分段长度_1"];
            const curW = cur["分段宽度_1"];
            const curH = cur["分段高度_1"];
            if (curL * curW > maxL * maxW) {
              max = cur;
            }

            if (curList.length - 1 === j) {
              const shipName = shipItem.shipNumber;
              const name = partItem.partNumber;
              const L = max["分段长度_1"];
              const W = max["分段宽度_1"];
              const H = max["分段高度_1"];
              updateTableData(shipItem, partItem);
              createParts(shipName, name, L, W, H, partItem);
            }
          });
        }
      }

      const names = [];
      // 创建堆放零件
      function createParts(shipName, name, L, W, H, partItem) {
        const color = getRandomColor();
        const url = createTextCanvas(shipName, name, color);
        const geometry = new THREE.BoxGeometry(L, H, W);
        const materials = [
          new THREE.MeshBasicMaterial({ color: color }), // right
          new THREE.MeshBasicMaterial({ color: color }), // left
          new THREE.MeshStandardMaterial({
            transparent: true,
            map: new THREE.TextureLoader().load(url),
            side: THREE.DoubleSide,
          }), // top
          new THREE.MeshBasicMaterial({ color: color }), // bottom
          new THREE.MeshBasicMaterial({ color: color }), // back
          new THREE.MeshBasicMaterial({ color: color }), // front
        ];
        const cube = new THREE.Mesh(geometry, materials);
        cube.name = shipName + name;
        const pos = partItem.startPos;
        const r = partItem.rotation;
        cube.position.set(0, 10, 0);
        if (pos) {
          cube.position.set(pos.x, pos.y, pos.z);
          if (!pos.y) {
            cube.position.y = 10;
          }
        }
        cube.scale.set(2, 2, 2);

        if (r) {
          if (r.x) {
            cube.rotation.x = Math.PI * r.x;
          }

          if (r.y) {
            cube.rotation.y = Math.PI * r.y;
          }

          if (r.z) {
            cube.rotation.z = Math.PI * r.z;
          }
        }

        scene.add(cube);
        names.push(cube.name);
        intersectsObjs.push(cube);

        // 触发起吊 partItem.startPos  partItem.position
        callLMD(cube, partItem.position).then(() => {
          arr.push({
            obj: cube,
            pos: partItem.assemblyPos,
          });
          findNext();
        });
      }

      //模型拾取
      function onDocumentMouseDown(event) {
        const mouseVector = new THREE.Vector3();
        const x = (event.layerX / container.offsetWidth) * 2 - 1;
        const y = -(event.layerY / container.offsetHeight) * 2 + 1;
        mouseVector.set(x, y, 0.5);

        raycaster.setFromCamera(mouseVector, camera);

        var intersects = raycaster.intersectObjects(intersectsObjs, true);

        if (intersects.length > 0) {
          const model = findParent(intersects[0].object);
          console.log(model);
          control.attach(model);
        }

        renderer.render(scene, camera);
      }

      // const names = ['HLB','tc','HLA','ship','CZ02S']
      function findParent(obj) {
        if (names.includes(obj.name)) {
          return obj;
        } else {
          return findParent(obj.parent);
        }
      }

      function callLMD(obj, targetPos) {
        return new Promise((resolve) => {
          targetPosition = targetPos;
          // 抓取
          const tZ = obj.position.z - lmd.offset;
          // 龙门架移动
          const moveTween = new TWEEN.Tween(lmd.position)
            .to({ z: tZ }, Math.abs(tZ - lmd.position.z) / lmdSpeed)
            .easing(TWEEN.Easing.Linear.None)
            .start();

          const v3 = lmd.worldToLocal(obj.position.clone());
          const tZ2 = v3.x - diaogou.offset;
          // 吊钩移动
          const moveTween2 = new TWEEN.Tween(diaogou.position)
            .to({ x: tZ2 }, Math.abs(tZ2 - diaogou.position.x) / dgSpeed)
            .easing(TWEEN.Easing.Linear.None)
            .onComplete(() => {
              TWEEN.remove(moveTween);
              TWEEN.remove(moveTween2);
              // 吊钩下降
              downAction._state = "get";
              downAction.reset();
              downAction.play();
            });
          moveTween.chain(moveTween2);
          downMixer.addEventListener("finished", downFinish);

          function downFinish() {
            if (downAction._state === "get") {
              targetMaterial = obj;
              targetAssemble = obj;
              let timer = setTimeout(() => {
                clearTimeout(timer);
                timer = null;
                // const objTween = new TWEEN.Tween(obj.position)
                //   .to({ y: 50 }, 2000)
                //   .easing(TWEEN.Easing.Linear.None)
                //   .start()
                //   .onComplete(() => {
                //     TWEEN.remove(objTween);
                //   });

                // 吊钩上升
                upAction.reset();
                upAction.play();
              }, 500);
            }
          }

          upMixer.addEventListener("finished", upFinish);

          function upFinish() {
            if (downAction._state === "get") {
              // 放置
              const _tZ = targetPos.z - lmd.offset;
              // 龙门架移动
              const moveTween3 = new TWEEN.Tween(lmd.position)
                .to({ z: _tZ }, Math.abs(_tZ - lmd.position.z) / lmdSpeed)
                .easing(TWEEN.Easing.Linear.None)
                .start()
                .onComplete(() => {
                  TWEEN.remove(moveTween3);
                });

              const v3 = lmd.worldToLocal(
                new THREE.Vector3(targetPos.x, targetPos.y, targetPos.z)
              );
              const _tZ2 = v3.x - diaogou.offset;
              // 吊钩移动
              const moveTween4 = new TWEEN.Tween(diaogou.position)
                .to({ x: _tZ2 }, Math.abs(_tZ2 - diaogou.position.x) / dgSpeed)
                .easing(TWEEN.Easing.Linear.None)
                .onComplete(() => {
                  TWEEN.remove(moveTween4);
                  // 下降
                  downAction._state = "put";
                  downAction.reset();
                  downAction.play();

                  // const objTween = new TWEEN.Tween(obj.position)
                  //   .to({ y: targetPos.y }, 2000)
                  //   .easing(TWEEN.Easing.Linear.None)
                  //   .start()
                  //   .onComplete(() => {
                  //     downAction.halt();
                  //     TWEEN.remove(objTween);

                  //     scene.add(obj);
                  //     obj.position.set(targetPos.x, targetPos.y, targetPos.z);

                  //     let timer = setTimeout(() => {
                  //       clearTimeout(timer);
                  //       timer = null;

                  //       upAction.reset();
                  //       upAction.time = 2 - downAction.time;
                  //       upAction.play();
                  //       downMixer.removeEventListener("finished", downFinish);
                  //       upMixer.removeEventListener("finished", upFinish);

                  //       resolve();
                  //     }, 500);
                  //   });
                });
              moveTween3.chain(moveTween4);
            } else {
              downMixer.removeEventListener("finished", downFinish);
              upMixer.removeEventListener("finished", upFinish);
              resolve();
            }
          }
        });
      }

      const th = `<tr>
        <th>序号</th>
        <th>船坞批次</th>
        <th>船号</th>
        <th>总段数量</th>
        <th>总段明细</th>
      </tr>`;
      function updateTableData(ship, part) {
        let shipItem = tableData.find(
          (item) => item.shipNumber === ship.shipNumber
        );
        if (!shipItem) {
          shipItem = {
            shipNumber: ship.shipNumber,
            totalSegments: 0,
            segmentsArr: [],
          };
          tableData.push(shipItem);
        }
        shipItem.totalSegments += 1;
        shipItem.segmentsArr.push(part.partNumber);

        let str = "";
        tableData.forEach((item, ind) => {
          str += `<tr>
            <td>${ind + 1}</td>
            <td>1</td>
            <td>${item.shipNumber}</td>
            <td>${item.totalSegments}</td>
            <td>${item.segmentsArr.join(",")}</td>
          </tr>`;
        });
        str = th + str;
        document.getElementById("table").innerHTML = str;
      }
    </script>
  </body>
</html>
